import threading
import subprocess
import sys
import shutil
from pathlib import Path
import tkinter as tk
from tkinter import messagebox


ROOT = Path(__file__).resolve().parents[1]
DATASET = ROOT / 'data' / 'room1'


def run_cmd(cmd):
    print('$', ' '.join(cmd))
    return subprocess.call(cmd)


def do_pipeline(log_widget: tk.Text, duration_sec: int, voxel: str, sdf_trunc: str, start_btn: tk.Button, view_pcd_btn: tk.Button, view_mesh_btn: tk.Button):
    try:
        # 清理上一次数据，保证每次采集干净
        if DATASET.exists():
            shutil.rmtree(DATASET, ignore_errors=True)
        DATASET.parent.mkdir(parents=True, exist_ok=True)
        try:
            view_pcd_btn.configure(state=tk.DISABLED)
            view_mesh_btn.configure(state=tk.DISABLED)
        except Exception:
            pass
        log_widget.insert(tk.END, f'开始采集 {duration_sec} 秒...\n'); log_widget.see(tk.END)
        ret = run_cmd([
            sys.executable, str(ROOT / 'tools' / 'rgbd_record_orbbec_openni.py'),
            '--out', str(DATASET), '--width', '640', '--height', '480', '--fps', '30',
            '--duration', str(duration_sec), '--fx', '494.17248433', '--fy', '494.94792223', '--cx', '317.00285128', '--cy', '241.76697033'
        ])
        if ret != 0:
            messagebox.showerror('错误', '采集失败，请检查相机和驱动')
            return

        if not (DATASET / 'associations.txt').exists():
            run_cmd([sys.executable, str(ROOT / 'tools' / 'build_associations.py'), '--dataset', str(DATASET)])

        log_widget.insert(tk.END, f'开始重建(体素={voxel}, 截断={sdf_trunc})...\n'); log_widget.see(tk.END)
        pcd = DATASET / 'tsdf_pointcloud_demo.ply'
        mesh = DATASET / 'tsdf_mesh_demo.ply'
        ret = run_cmd([
            sys.executable, str(ROOT / 'tools' / 'demo_tsdf_open3d_odometry.py'),
            '--dataset', str(DATASET), '--voxel', '0.02', '--sdf_trunc', '0.06', '--max_depth', '4.0', '--frame_step', '2', '--resize_scale', '0.75', '--median_ksize', '3', '--rotate180_z', '--center',
            '--out', str(pcd), '--out_mesh', str(mesh)
        ])
        if ret != 0:
            messagebox.showerror('错误', '重建失败')
            return

        try:
            if pcd.exists():
                view_pcd_btn.configure(state=tk.NORMAL)
            if mesh.exists():
                view_mesh_btn.configure(state=tk.NORMAL)
        except Exception:
            pass

        log_widget.insert(tk.END, '完成！点击“查看点云”或“查看网格”浏览结果\n'); log_widget.see(tk.END)
        # 自动打开结果以避免手动误操作时机
        try:
            target_to_open = mesh if mesh.exists() else pcd
            run_cmd([sys.executable, str(ROOT / 'tools' / 'view_geometry.py'), '--file', str(target_to_open)])
        except Exception:
            pass
        messagebox.showinfo('完成', '采集与重建完成')
    except Exception as e:
        messagebox.showerror('异常', str(e))
    finally:
        # 允许再次采集
        try:
            log_widget.after(0, lambda: start_btn.config(state=tk.NORMAL))
        except Exception:
            pass


def on_start(log_widget: tk.Text, btn: tk.Button, duration_var: tk.StringVar, view_pcd_btn: tk.Button, view_mesh_btn: tk.Button):
    btn.config(state=tk.DISABLED)
    try:
        duration_sec = int(duration_var.get())
        if duration_sec <= 0:
            duration_sec = 10
    except Exception:
        duration_sec = 10

    # 固定为“标准”密度
    voxel, sdf_trunc = ('0.02', '0.08')

    t = threading.Thread(target=do_pipeline, args=(log_widget, duration_sec, voxel, sdf_trunc, btn, view_pcd_btn, view_mesh_btn), daemon=True)
    t.start()


def view_file(path: Path):
    if not path.exists():
        messagebox.showwarning('提示', f'文件不存在: {path}')
        return
    run_cmd([sys.executable, str(ROOT / 'tools' / 'view_geometry.py'), '--file', str(path)])


def main():
    root = tk.Tk()
    root.title('动态连续 3D 扫描 (Dynamic Scan)')
    root.geometry('720x420')

    left = tk.Frame(root, width=200)
    left.pack(side=tk.LEFT, fill=tk.Y)

    right = tk.Frame(root)
    right.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

    tk.Label(left, text='采集时长(秒)').pack(padx=10, pady=(10, 0), fill=tk.X)
    duration_var = tk.StringVar(value='8')
    duration_entry = tk.Entry(left, textvariable=duration_var)
    duration_entry.pack(padx=10, pady=5, fill=tk.X)

    # 固定标准密度（体素0.02, 截断0.08）
    tk.Label(left, text='重建密度: 标准 (体素0.02, 截断0.08)').pack(padx=10, pady=(10, 0), fill=tk.X)

    start_btn = tk.Button(left, text='开始采集', height=2)
    start_btn.pack(padx=10, pady=10, fill=tk.X)

    view_pcd_btn = tk.Button(left, text='查看点云', state=tk.DISABLED, command=lambda: view_file(DATASET / 'tsdf_pointcloud_demo.ply'))
    view_pcd_btn.pack(padx=10, pady=10, fill=tk.X)

    view_mesh_btn = tk.Button(left, text='查看网格', state=tk.DISABLED, command=lambda: view_file(DATASET / 'tsdf_mesh_demo.ply'))
    view_mesh_btn.pack(padx=10, pady=10, fill=tk.X)

    log = tk.Text(right, wrap='word')
    log.pack(fill=tk.BOTH, expand=True)

    start_btn.config(command=lambda: on_start(log, start_btn, duration_var, view_pcd_btn, view_mesh_btn))

    root.mainloop()


if __name__ == '__main__':
    main()


